
package assdraw.view;



import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.AffineTransform;
import java.util.Vector;

import javax.swing.JPanel;

import assdraw.command.AddCommand;
import assdraw.command.LineCommand;
import assdraw.event.DrawingEvent;
import assdraw.event.DrawingListener;
import assdraw.model.Drawing;
import assdraw.model.Shape;



public class DrawingCanvas extends JPanel implements DrawingListener, MouseListener, MouseMotionListener, KeyListener
{
	private static final long serialVersionUID = 1L;
	
    private Drawing drawing;
    
    AffineTransform transformer = new AffineTransform();
    
    private Vector<Shape> selection;
    
    private AddCommand currentCommand = new LineCommand();
    
    private Visitor currentVisitor;

	private int oldMouseX;
	private int oldMouseY;

	private double zoomFactor = 1.0;

	private double translationX = 0.0;

	private double translationY = 0.0;

	private Color currentColor = Color.BLACK;
	
    
    public Color getCurrentColor() {
		return currentColor;
	}

	public void setCurrentColor(Color currentColor) {
		this.currentColor = currentColor;
	}

	public DrawingCanvas(){
    	
    	this(new Drawing(400, 400));
    	
    }
    
    public DrawingCanvas(Drawing d)
    {
        drawing = d;
        selection = new Vector<Shape>();        
        
        setFocusable(true);
        setFocusTraversalKeysEnabled(false);
        
        drawing.addDrawingListener(this);
        
        addMouseListener(this);
        addKeyListener(this);
    }

    @Override	
    public void paint(Graphics g)
    {
    	g.clearRect(0, 0, this.getWidth(), this.getHeight());
    	//g.drawLine(0, 0, 200, 200);
    	    	
    	AffineTransform transformer = new AffineTransform();
    	transformer.scale(zoomFactor, zoomFactor);
    	transformer.translate(translationX, translationY);    	    	    
    	Graphics2D g2d = (Graphics2D)g;
    	g2d.setTransform(transformer);
    	    	
    	currentVisitor = new ShapeDrawVisitor(g2d);
    	
    	
//    	g2d.setColor(new Color(0, 0, 0));   
        
        for(Shape s : drawing.getShapes()) {
            
            if(selection.contains(s)){
            	((ShapeDrawVisitor)currentVisitor).setInSelection(true);
            }else{
            	((ShapeDrawVisitor)currentVisitor).setInSelection(false);
            }
            
            s.accept(currentVisitor);
            
            //g2d.setColor(new Color(0, 0, 0));
            
        }
        
        //g.drawLine(0, 0, 200, 200);
    }   
    	
    public void update(DrawingEvent e)
    {
    	if(e.getKind() == DrawingEvent.SHAPE_GROUP){
    		selection.clear();
    	}
    	
        repaint();
        
    }
    
    /**
     * Selects or deselects the rectangle
     * @param r Rectangle
     */    
	public void addToSelection(Shape s) {
		if(! selection.contains(s))
			selection.add(s);
		
		repaint();
	}

	/**
	 * Getter for selected
	 * @return currently selected rectangle 
	 */
	public Vector<Shape> getSelected() {
		return selection;
	}

    
    @Override
    public void mouseClicked(MouseEvent e)
    {
    	this.requestFocus();
    	
    	Vector<Shape> v = drawing.getShapes();
    	for(int i=v.size()-1; i>=0; i--){
    		Shape s = v.get(i);
    		if(s.contains(getRealMouseX(e), getRealMouseY(e))){
    			if(!selection.contains(s))
    				selection.add(s);
    			repaint();
    			return;
    		}
    	}
    	
    	if(selection.isEmpty()){
    		currentCommand.execute(getRealMouseX(e), getRealMouseY(e), drawing, currentColor);
    	}else{
    		selection.clear();
    		repaint();
    	}
    }

	@Override
    public void mouseReleased(MouseEvent e)
    {
    	if(getMouseMotionListeners().length==1)
    		removeMouseMotionListener(getMouseMotionListeners()[0]);
    }
    
    @Override
    public void mouseEntered(MouseEvent e)
    {
        
    }
    
    @Override
    public void mouseExited(MouseEvent e)
    {
        
    }
    
    @Override
    public void mousePressed(MouseEvent e)
    {
    	if(!selection.isEmpty()){
    		
    		for(Shape s : selection){
    			if(s.contains(getRealMouseX(e), getRealMouseY(e))){
    				addMouseMotionListener(this);
    				oldMouseX = getRealMouseX(e);
    				oldMouseY = getRealMouseY(e);
    				break;
    			}
    		}
    		
    	}
    }
    
    
	@Override
	public void mouseDragged(MouseEvent e) {

		for(Shape s : selection){
			s.move(s.getX() + getRealMouseX(e) - oldMouseX, s.getY() + getRealMouseY(e) - oldMouseY);
		}
		oldMouseX = getRealMouseX(e);
		oldMouseY = getRealMouseY(e);
		
		repaint();
	}

	@Override
	public void mouseMoved(MouseEvent arg0) {

	}
	@Override
	public void keyPressed(KeyEvent e) {
		if(e.getKeyCode() == KeyEvent.VK_LEFT){
			translationX += 5;
		}else if(e.getKeyCode() == KeyEvent.VK_RIGHT){
			translationX -= 5;
		}else if(e.getKeyCode() == KeyEvent.VK_UP){
			translationY += 5;
		}else if(e.getKeyCode() == KeyEvent.VK_DOWN){
			translationY -= 5;
		}
		repaint();
	}
	@Override
	public void keyTyped(KeyEvent e) {
		
	}
	
	@Override
	public void keyReleased(KeyEvent e) {
		
		if(!selection.isEmpty()){
			if(e.getKeyCode() == KeyEvent.VK_B){
				for(Shape s : selection)
					drawing.sendToBack(s);
				return;
			}else if(e.getKeyCode() == KeyEvent.VK_F){
				for(Shape s : selection)
					drawing.bringToFront(s);
				return;
			}else if(e.getKeyCode() == KeyEvent.VK_DELETE){
				for(Shape s : selection)
					drawing.removeShape(s);
				selection.clear();
				return;
			}
		}
		
	}

	public void setCurrentCommand(AddCommand currentCommand) {
		this.currentCommand = currentCommand;
	}

	public AddCommand getCurrentCommand() {
		return currentCommand;
	}

	public void zoomOut() {
		zoomFactor *= 0.9;
		repaint();
	}

	public void zoomIn() {
		zoomFactor *= 1.1;
		repaint();
	}

	/**
	 * Decreases the size of shapes in selection by default percentage
	 */
	public void sizeDown() {
		for(Shape s : selection)
			s.scale(0.9, 0.9);
		repaint();
	}

	/**
	 * Increases the size of shapes in selection by default percentage
	 */
	public void sizeUp() {
		for(Shape s : selection)
			s.scale(1.1, 1.1);
		repaint();
	}

	/**
	 * Ungroups all shapes in current selection
	 */
	public void ungroup() {
		for(Shape s : selection)
			drawing.ungroup(s);
		selection.clear();
	}

	/**
	 * Groups all shapes in current selection in one group
	 */
	public void group() {
		drawing.group(selection);
		selection.clear();
	}
	
    private int getRealMouseX(MouseEvent e) {
		return (int)Math.round(e.getX()/zoomFactor - translationX);
	}
    
    private int getRealMouseY(MouseEvent e) {
		return (int)Math.round(e.getY()/zoomFactor - translationY);
	}

	public Drawing getDrawing() {
		return drawing;		
	}
	
}
